package org.lex.game.sudoku.model;

import java.util.Set;

import ms.sudoku.util.IntSet;

import org.lex.game.sudoku.model.technique.TestTech;
import org.lex.game.sudoku.model.visitor.DuplicatesNumberVisitor;
import org.lex.game.sudoku.model.visitor.ModelVisitors;
import org.lex.game.sudoku.model.visitor.NumberCountVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import common.Sudokus;

public class SudokuModelSupport {
	private final SudokuModel model;

	private final Logger log = LoggerFactory.getLogger(SudokuModelSupport.class);

	SudokuModelSupport(SudokuModel model) {
		this.model = model;
	}

	public CheckResult checkAll(boolean findAllInvalid) {
		CheckResult result = new CheckResult(true);
		for (int row = 0, gridSize = this.model.getGridSize(); row < gridSize; row++) {
			for (int col = 0; col < gridSize; col++) {
				CheckResult currentResult = this.checkCell(row, col, findAllInvalid);
				if (false == currentResult.isValid()) {
					if (false == findAllInvalid) {
						return currentResult;
					} else {
						result.merge(currentResult);
					}
				}
			}
		}
		return result;
	}

	// cell
	
	public boolean checkCanFillWith(int row, int col, int num) {
		if (Sudokus.notEmpty(this.model.get(row, col))) {
			return false;
		}
		NumberCountVisitor visitor = new NumberCountVisitor(num);
		// check if current num can fill in row
		int count = ModelVisitors.visitRow(this.model, visitor, row);
		boolean canFill = (0 == count);
		// check ... column
		if (true == canFill) {
			count = ModelVisitors.visitColumn(this.model, visitor, col);
			canFill = (0 == count);
		}
		// check ... block
		if (true == canFill) {
			count = ModelVisitors.visitBox(this.model, visitor, row, col);
			canFill = (0 == count);
		}
		return canFill;
	}
	/**
	 * 检查一个单元(row, column)所在的行、列、块中是否有重复数字
	 * 
	 * @param row
	 * @param column
	 * @param findAllInvalid
	 * @return
	 */
	public CheckResult checkCell(int row, int column, boolean findAllInvalid) {
		CheckResult result = null;
		if (true == findAllInvalid) {
			CheckResult rowResult = this.checkRow(row, true);
			CheckResult columnResult = this.checkColumn(column, true);
			CheckResult blockResult = this.checkBox(row, column, true);
			result = rowResult.merge(columnResult).merge(blockResult);
		} else {
			boolean valid = this.checkRow(row, false).isValid() && this.checkColumn(column, false).isValid()
					&& this.checkBox(row, column, false).isValid();
			result = new CheckResult(valid);
		}
		return result;
	}

	// row
	private CheckResult checkRow(int row, boolean findAll) {
		Set<Point> duplicates = ModelVisitors.visitRow(this.model, new DuplicatesNumberVisitor(findAll), row);
		CheckResult result = new CheckResult(duplicates);
		if (false == result.isValid() && log.isTraceEnabled()) {
			log.trace("row[" + row + "] INVALID! duplicates=" + duplicates + " --> " + result);
		}
		return result;
	}

	// column
	private CheckResult checkColumn(int column, boolean findAll) {
		Set<Point> duplicates = ModelVisitors.visitColumn(this.model, new DuplicatesNumberVisitor(findAll), column);
		CheckResult result = new CheckResult(duplicates);
		if (false == result.isValid() && log.isTraceEnabled()) {
			log.trace("column[" + column + "] INVALID! points=" + duplicates + " --> " + result);
		}
		return result;
	}

	// box
	private CheckResult checkBox(int row, int column, boolean findAll) {
		Set<Point> duplicates = ModelVisitors.visitBox(this.model, new DuplicatesNumberVisitor(findAll), row, column);
		CheckResult result = new CheckResult(duplicates);
		if (false == result.isValid() && log.isTraceEnabled()) {
			int boxSize = this.model.getBoxSize();
			int rowFrom = row / boxSize * boxSize;
			int rowTo = rowFrom + boxSize;
			int colFrom = column / boxSize * boxSize;
			int colTo = colFrom + boxSize;
			if (log.isTraceEnabled()) {
				log.trace(this.toString());
				log.trace("Point [" + row + ", " + column + "] belong to block [" + rowFrom + "," + colFrom + "]~["
						+ (rowTo - 1) + "," + (colTo - 1) + "] INVALID! --> " + result);
			}
		}
		return result;
	}

	/**
	 * 计算单元格的所有提示数字
	 * 
	 * @param row
	 * @param column
	 * @return
	 */
	public IntSet getHints(int row, int column) {
		IntSet results = new IntSet();

		// not an empty cell, so no hints
		if (Sudokus.notEmpty(this.model.get(row, column))) {
			return results;
		}

		for (int num = 1, max = model.getGridSize(); num <= max; num++) {
			boolean canFill = this.model.checkCanFillWith(row, column, num);
			if (true == canFill) {
				results.add(num);
			}
		}
		if (1 != results.size()) {
			IntSet copy = results.clone();
			results = new TestTech().test(model, row, column, results);
			if (1 == results.size()) {
				log.debug("TestTech works on cell(" + row + ", " + column + "): eliminate from " + copy + " to "
						+ results);
			}
		}
		return results;
	}
}
